/*
 * Copyright (C) 2018 Logos Technology Ltd.
 */

package com.logos.tool.log;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.logos.tool.ApplicationUtil;
import com.logos.tool.file.StorageUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * Abstract class that takes care of background threading the file log operation on Android.
 * implementing classes are free to directly perform I/O operations there.
 * Writes all logs to the disk with CSV format.
 */
public class DiskLog {

    private static final int MAX_BYTES = 500 * 1024; // 500K averages to a 4000 lines per file

    @NonNull
    private final WriteHandler mHandler;

    private static volatile DiskLog sSingleton;
    private final String mFolder;

    private DiskLog() {
        mFolder = StorageUtils.getCacheDirectory(ApplicationUtil.getContext(),
                "Logger").getAbsolutePath();
        HandlerThread ht = new HandlerThread("AndroidFileLogger." + mFolder);
        ht.start();
        this.mHandler = new DiskLog.WriteHandler(ht.getLooper(), mFolder, MAX_BYTES);
    }

    /**
     * 获取单例
     *
     * @return 实例
     */
    public static DiskLog getInstance() {
        if (sSingleton == null) {
            synchronized (DiskLog.class) {
                if (sSingleton == null) {
                    sSingleton = new DiskLog();
                }
            }
        }
        return sSingleton;
    }

    /**
     * getLogFile
     *
     * @return file
     */
    public File getLogFile() {
        return mHandler.getLogFile(mFolder, "logs");
    }

    /**
     * print log
     *
     * @param level   level
     * @param tag     tag
     * @param message message
     */
    public void log(int level, @Nullable String tag, @NonNull String message) {
        // do nothing on the calling thread, simply pass the tag/msg to the background thread
        mHandler.sendMessage(mHandler.obtainMessage(level, message));
    }

    static class WriteHandler extends Handler {

        @NonNull
        private final String mFolder;
        private final int mMaxFileSize;

        WriteHandler(@NonNull Looper looper, @NonNull String folder, int maxFileSize) {
            super(looper);
            this.mFolder = folder;
            this.mMaxFileSize = maxFileSize;
        }

        @SuppressWarnings("checkstyle:emptyblock")
        @Override
        public void handleMessage(@NonNull Message msg) {
            String content = (String) msg.obj;

            FileWriter fileWriter = null;
            File logFile = getLogFile(mFolder, "logs");

            try {
                fileWriter = new FileWriter(logFile, true);

                writeLog(fileWriter, content);

                fileWriter.flush();
                fileWriter.close();
            } catch (IOException e) {
                if (fileWriter != null) {
                    try {
                        fileWriter.flush();
                        fileWriter.close();
                    } catch (IOException e1) { /* fail silently */ }
                }
            }
        }

        /**
         * This is always called on a single background thread.
         * Implementing classes must ONLY write to the fileWriter and nothing more.
         * The abstract class takes care of everything else including close the stream and
         * catching IOException
         *
         * @param fileWriter an instance of FileWriter already initialised to the correct file
         */
        private void writeLog(@NonNull FileWriter fileWriter, @NonNull String content)
                throws IOException {

            fileWriter.append(content);
        }

        public File getLogFile(@NonNull String folderName, @NonNull String fileName) {

            File folder = new File(folderName);
            if (!folder.exists()) {
                // What if mFolder is not created, what happens then?
                folder.mkdirs();
            }

            int newFileCount = 0;
            File newFile;
            File existingFile = null;

            newFile = new File(folder, String.format("%s_%s.txt", fileName, newFileCount));
            while (newFile.exists()) {
                existingFile = newFile;
                newFileCount++;
                newFile = new File(folder, String.format("%s_%s.txt", fileName, newFileCount));
            }

            if (existingFile != null) {
                if (existingFile.length() >= mMaxFileSize) {
                    return newFile;
                }
                return existingFile;
            }

            return newFile;
        }
    }

}
